/**
 * Copyright 2019 吉鼎科技.

 * <p>
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * <p>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p>
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package cn.easyplatform.web.controller;

import cn.easyplatform.EasyPlatformWithLabelKeyException;
import cn.easyplatform.lang.Strings;
import cn.easyplatform.messages.request.BeginRequestMessage;
import cn.easyplatform.messages.vos.*;
import cn.easyplatform.messages.vos.h5.MessageVo;
import cn.easyplatform.spi.service.TaskService;
import cn.easyplatform.type.*;
import cn.easyplatform.web.WebApps;
import cn.easyplatform.web.contexts.Contexts;
import cn.easyplatform.web.dialog.MessageBox;
import cn.easyplatform.web.layout.IMainTaskBuilder;
import cn.easyplatform.web.layout.LayoutManagerFactory;
import cn.easyplatform.web.listener.LogoutListener;
import cn.easyplatform.web.listener.SessionValidationScheduler;
import cn.easyplatform.web.message.impl.MessageHandler;
import cn.easyplatform.web.service.ServiceLocator;
import cn.easyplatform.web.task.BackendException;
import cn.easyplatform.web.task.EventSupport;
import cn.easyplatform.web.task.MainTaskSupport;
import cn.easyplatform.web.task.OperableHandler;
import cn.easyplatform.web.task.zkex.ListSupport;
import cn.easyplatform.web.task.zkex.list.PanelSupport;
import cn.easyplatform.web.utils.WebUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.zkoss.util.resource.Labels;
import org.zkoss.xel.util.SimpleResolver;
import org.zkoss.zk.ui.Component;
import org.zkoss.zk.ui.Executions;
import org.zkoss.zk.ui.Page;
import org.zkoss.zk.ui.Sessions;
import org.zkoss.zk.ui.event.Events;
import org.zkoss.zk.ui.metainfo.ComponentInfo;
import org.zkoss.zk.ui.util.Clients;
import org.zkoss.zk.ui.util.Composer;
import org.zkoss.zk.ui.util.ComposerExt;
import org.zkoss.zul.Div;
import org.zkoss.zul.Label;
import org.zkoss.zul.Timer;

import javax.servlet.http.HttpServletRequest;
import java.util.*;


/**
 * @author <a href="mailto:davidchen@epclouds.com">littleDog</a> <br/>
 * @since 2.0.0 <br/>
 */
public class MobileLayoutController implements Composer<Component>,
        ComposerExt<Component>, LayoutController {

    private Logger log = LoggerFactory.getLogger(MobileLayoutController.class);

    private Component container;

    private Div header;

    private List<RoleVo> roles;

    private Label lblTitle;

    private Stack<MainTaskSupport> stack;

    private MessageHandler messageHandler;

    @Override
    public void doAfterCompose(Component comp) throws Exception {
        container = comp.getFellowIfAny("gv5container");
        AuthorizationVo av = Contexts.getUserAuthorzation();
        if (av != null) {
            roles = new ArrayList<>();
            for (RoleVo rv : av.getRoles())
                roles.add(rv);
            if (av.getAgents() != null) {
                for (AgentVo agent : av.getAgents()) {
                    for (RoleVo rv : agent.getRoles())
                        roles.add(rv);
                }
            }
            WebApps.me().subscribe(comp.getDesktop());
        }
        if (WebApps.me().isSessionKeepAlive())
            SessionValidationScheduler.start(comp);
    }

    @Override
    public ComponentInfo doBeforeCompose(Page page, Component component, ComponentInfo componentInfo) throws Exception {
        EnvVo env = Contexts.getEnv();
        if (env == null) {
            Executions.sendRedirect("/login.go");
        } else {
            LoginVo user = Contexts.getUser();
            if (user == null) {
                if (Contexts.getEnv() != null)
                    Contexts.getEnv().setMainPage(null);
                Executions.sendRedirect("/login.go");
            } else {
                page.setTitle(env.getTitle());
                Map<String, Object> vars = new HashMap<String, Object>();
                vars.put("$easyplatform", this);
                page.addVariableResolver(new SimpleResolver(vars));
                if (!Strings.isBlank(user.getTooltip()))
                    Clients.showNotification(user.getTooltip(),
                            "warning", null, "start_before", -1, true);
                Clients.confirmClose(Labels.getLabel("confirm.close"));
                if (user.getType() == UserType.TYPE_ANONYMOUS) {
                    HttpServletRequest req = (HttpServletRequest) Executions.getCurrent().getNativeRequest();
                    String url = req.getRequestURI().substring(
                            req.getContextPath().length());
                    env.setLogoutUrl(url + (req.getQueryString() == null ? "" : "?" + req.getQueryString()));
                }
                stack = new Stack<>();
            }
        }
        return componentInfo;
    }

    /**
     * 绑定header
     *
     * @param c
     */
    public void header(Div c) {
        this.header = c;
        header.setVisible(false);
    }

    /**
     * 绑定标题
     *
     * @param c
     */
    public void title(Label c) {
        this.lblTitle = c;
    }

    /**
     * 登出
     */
    public void logout() {
        LogoutListener.logout(Contexts.getEnv());
    }

    /**
     * 消息
     *
     * @param comp
     */
    public void message(Component comp) {
        messageHandler = new MessageHandler(MessageVo.TYPE_NOTICE, comp);
    }

    /**
     * 消息
     *
     * @param comp
     */
    public void message(Component comp, String type) {
        messageHandler = new MessageHandler(type, comp);
    }

    /**
     * 待办事项
     *
     * @param comp
     */
    public void bpm(Component comp) {
        new MessageHandler(MessageVo.TYPE_TASK, comp);
    }

    /**
     * @param id
     */
    public void bpm(Object id) {
        if (container == null)
            WebUtils.doBpm(Executions.getCurrent().getDesktop().getFirstPage().getFirstRoot(), id);
        else
            WebUtils.doBpm(container, id);
    }

    /**
     * 消息数减1
     */
    public void reduce() {
        if (messageHandler != null)
            messageHandler.reduce();
    }


    /**
     * 返回上一级功能
     */
    public void prev() {
        if (container == null)
            return;
        MainTaskSupport builder = stack.pop();
        builder.close(false);
        builder = null;
        builder = stack.peek();
        container.getChildren().clear();
        try {
            if (stack.size() == 1)
                header.setVisible(false);
            else
                lblTitle.setValue(builder.getName());
            builder.prev(Integer.MAX_VALUE);
        } catch (EasyPlatformWithLabelKeyException ex) {
            builder.close(false);
            showError(container,
                    Labels.getLabel(ex.getMessage(), ex.getArgs()));
        } catch (BackendException ex) {
            builder.close(false);
            showError(container, ex.getMsg().getCode() + ":"
                    + ex.getMsg().getBody());
        } catch (Exception ex) {
            if (log.isErrorEnabled())
                log.error("build page:", ex);
            builder.close(false);
            showError(container,
                    ex.getMessage());
        }
    }

    public void go(Object obj, int mode) {
        String taskId = null;
        if (obj instanceof Component)
            taskId = (String) ((Component) obj).getAttribute("id");
        else
            taskId = (String) obj;
        if (!Strings.isBlank(taskId)) {
            if (container == null) {
                TaskVo tv = new TaskVo(taskId);
                tv.setOpenModel(Constants.OPEN_MODAL);
                doTask(tv, null);
            } else {
                MainTaskSupport builder = stack.peek();
                container.getChildren().clear();
                builder = doTask(createTask(taskId, mode), builder.getId());
                if (header != null)
                    header.setVisible(true);
                if (lblTitle != null) {
                    if (builder != null) {
                        lblTitle.setValue(builder.getName());
                        stack.push(builder);
                    } else
                        lblTitle.setValue(Labels.getLabel("message.dialog.title.error"));
                } else if (builder != null)
                    stack.push(builder);
            }
        }
    }

    /**
     * 运行功能
     *
     * @param obj
     */
    public void go(Object obj) {
        go(obj, Constants.OPEN_NORMAL);
    }

    /**
     * 运行功能
     *
     * @param tv
     * @param parent
     */
    public void go(TaskVo tv, EventSupport parent) {
        if (container == null)
            tv.setOpenModel(Constants.OPEN_MODAL);
        if (tv.getOpenModel() != Constants.OPEN_MODAL) {
            tv.setOpenModel(Constants.OPEN_NORMAL);
            container.getChildren().clear();
            header.setVisible(true);
        }
        MainTaskSupport builder = doTask(tv, parent.getId());
        if (builder != null) {
            if (tv.getOpenModel() != Constants.OPEN_MODAL) {
                lblTitle.setValue(builder.getName());
                stack.push(builder);
            }
            if (parent instanceof PanelSupport) {
                ListSupport ls = ((PanelSupport) parent).getList();
                if (ls instanceof OperableHandler)
                    ((IMainTaskBuilder) builder).setHost((OperableHandler) ls);
                else
                    ((IMainTaskBuilder) builder).setHost(ls.getMainHandler());
            } else if (parent instanceof OperableHandler)
                ((IMainTaskBuilder) builder).setHost((OperableHandler) parent);
            else if (parent instanceof ListSupport)
                ((IMainTaskBuilder) builder).setHost(((ListSupport) parent).getMainHandler());
        } else if (tv.getOpenModel() != Constants.OPEN_MODAL) {
            lblTitle.setValue(Labels.getLabel("message.dialog.title.error"));
        }
    }

    /**
     * 主页功能
     *
     * @param taskId
     */
    public void main(String taskId) {
        if (container == null)
            go(taskId);
        else {
            for (MainTaskSupport builder : stack)
                builder.close(false);
            header.setVisible(false);
            container.getChildren().clear();
            stack.clear();
            MainTaskSupport builder = doTask(createTask(taskId, Constants.OPEN_NORMAL), null);
            if (builder != null)
                stack.push(builder);
        }
    }

    /**
     * 从主页执行
     *
     * @param taskId
     */
    public void home(String taskId) {
        if (container != null) {
            container.getChildren().clear();
            Timer timer = new Timer();
            timer.setRepeats(false);
            timer.setDelay(100);
            timer.setRunning(true);
            HttpServletRequest req = (HttpServletRequest) Executions.getCurrent().getNativeRequest();
            Enumeration<String> ems = req.getParameterNames();
            List<FieldVo> args = new ArrayList<>();
            while (ems.hasMoreElements()) {
                String name = ems.nextElement();
                if (!"tid".equals(name) && !"app".equals(name))
                    args.add(new FieldVo(name, FieldType.VARCHAR, req.getParameter(name)));
            }
            timer.addEventListener(Events.ON_TIMER, event -> {
                TaskVo tv = createTask(taskId, Constants.OPEN_NORMAL);
                tv.setVariables(args);
                MainTaskSupport builder = doTask(tv, null);
                if (builder != null)
                    stack.push(builder);
                event.getTarget().detach();
            });
            timer.setParent(container);
        }
    }

    /**
     * 创建默认的task请求对象
     *
     * @param taskId
     * @return
     */
    private TaskVo createTask(String taskId, int mode) {
        TaskVo tv = new TaskVo(taskId);
        AuthorizationVo av = Contexts.getUserAuthorzation();
        if (av != null) {
            if (!av.getRoles().isEmpty()) {
                String defaultRoleId = av.getRoles().get(0).getId();
                tv.setRoleId(defaultRoleId);
            }
        }
        tv.setOpenModel(mode);
        return tv;
    }

    /**
     * 执行功能
     *
     * @param tv
     * @return
     */
    private MainTaskSupport doTask(TaskVo tv, String parentId) {
        BeginRequestMessage req = new BeginRequestMessage(tv);
        req.setId(parentId);
        TaskService mtc = ServiceLocator
                .lookup(TaskService.class);
        IResponseMessage<?> resp = mtc.begin(req);
        if (resp.isSuccess()) {
            MainTaskSupport builder = null;
            try {
                AbstractPageVo pv = (AbstractPageVo) resp.getBody();
                builder = (MainTaskSupport) LayoutManagerFactory
                        .createLayoutManager()
                        .getMainTaskBuilder(container == null ? Executions.getCurrent().getDesktop().getFirstPage().getFirstRoot() : container,
                                tv.getId(), pv);
                ((IMainTaskBuilder) builder).build();
            } catch (EasyPlatformWithLabelKeyException ex) {
                builder.close(false);
                showError(container,
                        Labels.getLabel(ex.getMessage(), ex.getArgs()));
            } catch (BackendException ex) {
                builder.close(false);
                showError(container, ex.getMsg().getCode() + ":"
                        + ex.getMsg().getBody());
            } catch (Exception ex) {
                if (log.isErrorEnabled())
                    log.error("build page:", ex);
                builder.close(false);
                showError(container,
                        ex.getMessage());
            }
            return builder;
        } else {
            showError(container, resp.getCode() + ":" + resp.getBody());
        }
        return null;
    }

    /**
     * 显示执行功能错误信息
     *
     * @param parent
     * @param msg
     */
    private void showError(Component parent, String msg) {
        if (parent == null) {
            MessageBox.showMessage(Labels.getLabel("message.dialog.title.error"), msg);
        } else {
            Div div = new Div();
            div.setClass("alert alert-danger");
            div.setStyle("margin-top:30px");
            Label label = new Label();
            label.setValue(msg);
            label.setParent(div);
            div.setParent(parent);
        }
    }

    /**
     * 是否是移动端页面
     *
     * @return
     */
    public boolean isMobile() {
        return "mil".equals(Contexts.getEnv().getDeviceType());
    }

    /**
     * 当前机构
     *
     * @return
     */
    public String getOrgId() {
        AuthorizationVo av = (AuthorizationVo) Sessions.getCurrent().getAttribute(Contexts.PLATFORM_USER_AUTHORIZATION);
        return av.getOrgId();
    }

    /**
     * 机构名称
     *
     * @return
     */
    public String getOrgName() {
        AuthorizationVo av = (AuthorizationVo) Sessions.getCurrent().getAttribute(Contexts.PLATFORM_USER_AUTHORIZATION);
        return av.getOrgName();
    }

    /**
     * 判断给定的角色是否在当前用户中
     *
     * @param roles
     * @return
     */
    public boolean isRole(String roles) {
        AuthorizationVo av = Contexts.getUserAuthorzation();
        String[] array = roles.split(",");
        for (RoleVo rv : av.getRoles()) {
            for (int i = 0; i < array.length; i++) {
                if (rv.getId().equals(array[i]))
                    return true;
            }
        }
        if (av.getAgents() != null && !av.getAgents().isEmpty()) {
            for (AgentVo agent : av.getAgents()) {
                for (RoleVo rv : agent.getRoles()) {
                    for (int i = 0; i < array.length; i++) {
                        if (rv.getId().equals(array[i]))
                            return true;
                    }
                }
            }
        }
        return false;
    }

    public List<RoleVo> getRoles() {
        return roles;
    }

    /**
     * 当前用户
     *
     * @return
     */
    public LoginVo getUser() {
        return Contexts.getUser();
    }

    @Override
    public void doBeforeComposeChildren(Component component) throws Exception {
    }

    @Override
    public boolean doCatch(Throwable throwable) throws Exception {
        return false;
    }

    @Override
    public void doFinally() throws Exception {
    }
}
